package com.gitee.sparta.core.cluster.balance;

import cn.hutool.core.collection.CollUtil;
import com.gitee.sparta.common.helpers.IsolationHelper;
import com.gitee.sparta.core.cluster.ClusterCache;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * <p>
 *      数组随机算法
 * </p>
 *
 * @author zyred
 * @since v 1.0.0
 */
@Slf4j
public class RandomLoadBalance implements LoadBalance {

    private static volatile int size = 0;
    private final List<String> ISOLATIONS = new ArrayList<>();
    private final static Random RANDOM = new Random();
    private static RandomLoadBalance instance;
    private static int next = 0;

    private RandomLoadBalance () {}

    @Override
    public String balance() {
        if (CollUtil.isEmpty(ISOLATIONS)) {
            return null;
        }
        return ISOLATIONS.get(next);
    }

    @Override
    public synchronized void addNode(String isolation, Integer sid) {
        Set<String> temp = new HashSet<>(ISOLATIONS);
        temp.add(isolation);
        ISOLATIONS.clear();
        ISOLATIONS.addAll(temp);

        size ++;
        log.info("[Sparta] Add random loadBalance, sid: {}, isolation: {}", sid, isolation);
    }

    @Override
    public synchronized void removeNode(Integer sid) {
        if (ISOLATIONS.remove(IsolationHelper.isolation(sid))) {
            size --;
        }
    }

    @Override
    public void switchover() {
        next = RANDOM.nextInt(ISOLATIONS.size());
    }

    @Override
    public void switchover(boolean jump, int size) {
        this.switchover();
        if (!jump && this.size() > size) {
            return;
        }
        while (Objects.equals(ISOLATIONS.get(next), ClusterCache.leaderIsolation)) {
            this.switchover();
        }
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean contains(String isolation) {
        return ISOLATIONS.contains(isolation);
    }

    public static RandomLoadBalance getRandomLoadBalanceInstance () {
        synchronized (NodeLoadBalance.class) {
            if (Objects.isNull(instance)) {
                synchronized (NodeLoadBalance.class) {
                    instance = new RandomLoadBalance();
                }
            }
        }
        return instance;
    }

}
